package com.zycfc.zsf.boot.util.bean;

import java.util.*;
import java.util.concurrent.*;

public abstract class Concurrents
{
    public static long timedWait(final Object obj, final long timeout) throws InterruptedException {
        return timedWait(obj, timeout, TimeUnit.MILLISECONDS);
    }
    
    public static long timedWait(final Object obj, final long timeout, final TimeUnit unit) throws InterruptedException {
        if (obj == null) {
            throw new IllegalArgumentException("invalid parameter obj");
        }
        if (unit == null) {
            throw new IllegalArgumentException("invalid parameter unit");
        }
        if (timeout <= 0L) {
            return 0L;
        }
        final long now = System.nanoTime();
        synchronized (obj) {
            unit.timedWait(obj, timeout);
        }
        final long elapsedTime = System.nanoTime() - now;
        return substract(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
    }
    
    public static long timedJoin(final Thread thread, final long timeout) throws InterruptedException {
        return timedJoin(thread, timeout, TimeUnit.MILLISECONDS);
    }
    
    public static long timedJoin(final Thread thread, final long timeout, final TimeUnit unit) throws InterruptedException {
        if (thread == null) {
            throw new IllegalArgumentException("invalid parameter thread");
        }
        if (unit == null) {
            throw new IllegalArgumentException("invalid parameter unit");
        }
        if (timeout <= 0L) {
            return 0L;
        }
        final long now = System.nanoTime();
        unit.timedJoin(thread, timeout);
        final long elapsedTime = System.nanoTime() - now;
        return substract(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
    }
    
    public static long delay(final long timeout) throws InterruptedException {
        return delay(timeout, TimeUnit.MILLISECONDS);
    }
    
    public static long delay(final Date date) throws InterruptedException {
        if (date == null) {
            throw new IllegalArgumentException("invalid parameter date");
        }
        return delay(date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }
    
    public static long delay(final long timeout, final TimeUnit unit) throws InterruptedException {
        if (unit == null) {
            throw new IllegalArgumentException("invalid parameter unit");
        }
        if (timeout <= 0L) {
            return 0L;
        }
        final Object object = new Object();
        final long now = System.nanoTime();
        synchronized (object) {
            unit.timedWait(object, timeout);
        }
        final long elapsedTime = System.nanoTime() - now;
        return substract(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
    }
    
    public static long shutdownAndAwaitTermination(final ExecutorService exec, final long timeout) throws InterruptedException {
        return shutdownAndAwaitTermination(exec, timeout, TimeUnit.MILLISECONDS);
    }
    
    public static long shutdownAndAwaitTermination(final ExecutorService exec, final long timeout, final TimeUnit unit) throws InterruptedException {
        if (exec == null) {
            throw new IllegalArgumentException("invalid parameter exec");
        }
        if (unit == null) {
            throw new IllegalArgumentException("invalid parameter unit");
        }
        if (timeout <= 0L) {
            return 0L;
        }
        final long now = System.nanoTime();
        if (!exec.isShutdown()) {
            exec.shutdown();
        }
        exec.awaitTermination(timeout, unit);
        final long elapsedTime = System.nanoTime() - now;
        return substract(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
    }
    
    public static <K, V> V getOrCreate(final ConcurrentHashMap<K, V> m, final K k, final BeanCreator<V> creator) {
        V v = m.get(k);
        if (v != null) {
            return v;
        }
        v = creator.create();
        final V r = m.putIfAbsent(k, v);
        if (r != null) {
            return r;
        }
        return v;
    }
    
    public static <K, V> V putIfAbsent(final ConcurrentMap<K, V> map, final K key, final V value) {
        final V result = map.putIfAbsent(key, value);
        return (result != null) ? result : value;
    }
    
    private static long substract(final long l1, final long l2) {
        final long r = ((l1 < 0L) ? 0L : l1) - ((l2 < 0L) ? 0L : l2);
        return (r < 0L) ? 0L : r;
    }
}
